﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;
using Rung.Net.Events;
using Rung.Net.Enums;

namespace Rung.Net.Base
{
    /// <summary>
    /// Abstract generic base class providing core stream functionality.
    /// </summary>
    /// <typeparam name="T">The type of data communicated.</typeparam>
    public abstract class NetBaseStream<T> : IDisposable
    {
        protected Thread thread;
        protected NetworkStream stream;
        public int NetBasePackageMaxLength
        {
            get;
            protected set;
        }
        /// <summary>
        /// 一个 int 型数字所占用的空间（单位为字节——byte）。
        /// </summary>
        public const int IntLength = 4;
        /// <summary>
        /// 一个 byte 型数字所占用的空间（单位为位——bit）。
        /// </summary>
        public const int ByteLength = 8;

        #region Properties
        /// <summary>
        /// Gets the streams guid (identifier used by server).
        /// </summary>
        public Guid Guid
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the number of bytes sent.
        /// </summary>
        public long DataSent
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the number of bytes received.
        /// </summary>
        public long DataReceived
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets a value indicating whether the stream is active.
        /// </summary>
        public bool IsActive
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the remote endpoint.
        /// </summary>
        public EndPoint EndPoint
        {
            get;
            private set;
        }

        /// <summary>
        /// Gets the tick rate.
        /// </summary>
        public int TickRate
        {
            get;
            set;
        } 
        #endregion

        #region Events
        /// <summary>
        /// Occurs when the stream is started.
        /// </summary>
        public event NetStreamStartedEventHandler OnStarted;

        /// <summary>
        /// Occurs when the stream is stopped.
        /// </summary>
        public event NetStreamStoppedEventHandler OnStopped;

        /// <summary>
        /// Occurs when data received from the stream.
        /// </summary>
        public event NetStreamReceivedEventHandler<T> OnReceived; 
        #endregion

        private NetBaseStream() { }

        /// <summary>
        /// Initializes a new instance of the NetBaseStream class.
        /// </summary>
        /// <param name="stream">The network stream to stream from.</param>
        /// <param name="endpoint">The remote endpoint.</param>
        public NetBaseStream(NetworkStream stream, EndPoint endpoint)
            : this(stream, endpoint, 1024)
        { }

        /// <summary>
        /// Initializes a new instance of the NetBaseStream class.
        /// </summary>
        /// <param name="stream">The network stream to stream from.</param>
        /// <param name="endpoint">The remote endpoint.</param>
        /// <param name="packageMaxLength">每次传输所能容纳最大字符数</param>
        public NetBaseStream(NetworkStream stream, EndPoint endpoint, int packageMaxLength)
        {
            Guid = System.Guid.NewGuid();
            IsActive = false;
            EndPoint = endpoint;
            TickRate = 1;

            this.stream = stream;
            this.NetBasePackageMaxLength = packageMaxLength;
        }

        /// <summary>
        /// Starts the stream.
        /// </summary>
        public void Start()
        {
            IsActive = true;

            thread = new Thread(new ThreadStart(ThreadedReceive));
            thread.Start();

            if (OnStarted != null)
                OnStarted(this, new NetStreamStartedEventArgs(Guid));
        }

        /// <summary>
        /// Stops the stream manually locally.
        /// </summary>
        public void Stop()
        {
            Stop(NetStoppedReason.Manually);
        }

        /// <summary>
        /// Stops the stream with a specific reason.
        /// </summary>
        /// <param name="reason">The stop reason.</param>
        protected void Stop(NetStoppedReason reason)
        {
            if (!IsActive) return;

            IsActive = false;
            stream.Close();

            if (OnStopped != null)
                OnStopped(this, new NetStreamStoppedEventArgs(Guid, reason));
        }

        /// <summary>
        /// Sends strictly typed data to the stream.
        /// </summary>
        /// <param name="data">The type of data.</param>
        public abstract void Send(T data);

        /// <summary>
        /// 将一组原始的字节数组发送到流。
        /// </summary>
        /// <param name="data">原始的字节数组。</param>
        /// <param name="isFlush">是否刷新流中的数据（stream.Flush）</param>
        protected void SendRaw(byte[] data, bool isFlush)
        {
            byte[] bytes = data;
            if (IsActive && stream.CanWrite)
            {
                try
                {
                    stream.Write(bytes, 0, bytes.Length);
                    if (isFlush)
                    {
                        // MSDN 原话：但是因为 NetworkStream 不进行缓冲处理，所以此方法对网络流无效。
                        // http://msdn.microsoft.com/zh-cn/library/system.net.sockets.networkstream.flush
                        // 太鸡巴失望了。
                        stream.Flush();
                    }

                    DataSent += bytes.LongLength;
                }
                catch (SocketException ex)
                {
                    Stop(NetStoppedReason.Remote);/////////////////////
                    return;
                }
            }
        }

        /// <summary>
        /// 将一组原始的字节数组发送到流。
        /// </summary>
        /// <param name="data">原始的字节数组。</param>
        protected void SendRaw(byte[] data)
        {
            this.SendRaw(data, true);
        }

        /// <summary>
        /// 抽象方法。
        /// 当接收到新的数据包时，在不同的子类中，应有不同的处理逻辑。
        /// 此方法为类内部调用，主要目的为缓存合并数据小包（raw）。
        /// 
        /// Should be implemented for handling raw bytes received from the stream.
        /// </summary>
        protected abstract void ReceivedRaw(byte[] bytes);

        /// <summary>
        /// The threaded receive loop.
        /// </summary>
        protected void ThreadedReceive()
        {
            while (IsActive && stream.CanRead)
            {
                Thread.Sleep(TickRate);
                byte[] buffer = new byte[this.NetBasePackageMaxLength];
                int recvLength = 0;

                try
                {
                    recvLength = stream.Read(buffer, 0, buffer.Length);
                    if (recvLength == 0)
                    {
                        Stop(NetStoppedReason.Remote);
                        return;
                    }
                    DataReceived += recvLength;
                }
                catch (System.IO.IOException)
                {
                    Stop(NetStoppedReason.Remote);
                    return;
                }
                catch (Exception)
                {
                    Stop(NetStoppedReason.Exception);
                    //throw ex;
                }

                // 当接收的数据长度小于 buffer 长度时，将 buffer 裁剪为合适的长度。
                if (recvLength < this.NetBasePackageMaxLength)
                {
                    byte[] newBuffer = new byte[recvLength];
                    System.Buffer.BlockCopy(buffer, 0, newBuffer, 0, recvLength);
                    buffer = newBuffer;
                }

                ReceivedRaw(buffer);
            }

            Stop(NetStoppedReason.Manually);
        }

        /// <summary>
        /// Raises the OnReceived event.
        /// </summary>
        /// <param name="data">The data associated with the event.</param>
        protected void RaiseOnReceived(T data)
        {
            if (OnReceived != null)
                OnReceived(this, new NetStreamReceivedEventArgs<T>(Guid, data));
        }

        #region IDisposable 成员

        public void Dispose()
        {
            this.Stop();
            stream.Dispose();
        }

        #endregion
    }
}
